/*
 * Component.java  0.4.0 / Oct 26, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing; // NOPMD

import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Locale;
import java.util.ResourceBundle;

import com.cute4j.core.AbstractDisposable;
import com.cute4j.core.events.EventListenerList;
import com.cute4j.swing.events.ComponentEvent;
import com.cute4j.swing.events.ComponentListener;
import com.cute4j.swing.events.ContextMenuEvent;
import com.cute4j.swing.events.FocusEvent;
import com.cute4j.swing.events.FocusListener;
import com.cute4j.swing.events.KeyEvent;
import com.cute4j.swing.events.KeyListener;
import com.cute4j.swing.events.MouseEvent;
import com.cute4j.swing.events.MouseListener;
import com.cute4j.swing.events.MouseMotionListener;
import com.cute4j.swing.events.MouseWheelEvent;
import com.cute4j.swing.events.MouseWheelListener;
import com.cute4j.swing.events.PropertyChangeSupport;
import com.cute4j.swing.events.SwingEvent;
import com.cute4j.swing.graphics.Color;
import com.cute4j.swing.graphics.Dimension;
import com.cute4j.swing.graphics.Font;
import com.cute4j.swing.graphics.FontMetrics;
import com.cute4j.swing.graphics.Graphics;
import com.cute4j.swing.graphics.Insets;
import com.cute4j.swing.graphics.Point;
import com.cute4j.swing.graphics.Rectangle;
import com.cute4j.swing.properties.ComponentProperties;

/**
 * The base class of all Swing components.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public abstract class Component extends AbstractDisposable
                                implements ComponentProperties {

// Package-Private Static Fields. ----------------------------------------------

  static final Component[] EMPTY_COMPONENT_ARRAY = new Component[0];

// Static Initialization. ------------------------------------------------------

  static {
    Swing.initialize();
    _initialize();
  }

// Package-Private Fields. -----------------------------------------------------

  String name;
  boolean enabled;
  boolean displayable;
  boolean visible;
  boolean windowShown;
  boolean valid;
  boolean focusable;
  boolean focused;
  long eventMask;
  Rectangle bounds;
  Dimension preferredSize;
  boolean preferredSizeSet;
  Dimension minimumSize;
  Dimension maximumSize;
  Dimension fixedSize;
  boolean resizable;
  Insets insets;
  boolean opaque;
  boolean cursorSet;
  Cursor cursor;
  boolean updatesEnabled;
  boolean backgroundSet;
  Color background;
  boolean foregroundSet;
  Color foreground;
  boolean fontSet;
  Font font;
  FontMetrics fontMetrics;
  StyleSheet styleSheet;
  boolean styleSheetSet;
  Locale locale;
  String resourceBundleName;
  ComponentOrientation componentOrientation;
  String toolTipText;
  String statusTipText;
  String whatsThisText;
  PropertyChangeSupport changeSupport;
  HashMap clientProperties;
  Component owner;
  Container parent;
  Menu contextMenu;

// Package-Private "Native" Fields. --------------------------------------------

  long _delegate;
  SwingGraphics _graphics;

// Protected Fields. -----------------------------------------------------------

  protected final EventListenerList listenerList;

// Protected Constructors. -----------------------------------------------------

  /**
   * Constructs new Component object.
   */
  protected Component() {
    this(true);
  }

  /**
   * Constructs new Component object.
   *
   * @param name the component name
   */
  protected Component(String name) {
    this(true);
    if (name != null) {
      this.name = name;
      _setName(_delegate, name); // NOPMD
    }
  }

// Protected by CHUCK NORRIS"! -------------------------------------------------
  
  /**
   * Constructs new Component object.
   * <p>
   *  <tt>
   *   NOTE: Use this constructor only if you implement a native component.
   *  </tt>
   * </p>
   *
   * @param initNative initialize native fields
   */
  protected Component(boolean initNative) {
    enabled = true;
    displayable = true;
    valid = true;
    focusable = true;
    resizable = true;
    updatesEnabled = true;
    bounds = new Rectangle();
    componentOrientation = ComponentOrientation.UNKNOWN;
    listenerList = new EventListenerList();
    if (initNative) {
      _delegate = _create(); // NOPMD
      if (_delegate == 0) {
        throw new OutOfMemoryError("Component creation failed");
      }
    }
    if (Swing.useStyleSheet()) {
      setStyleSheetProperties(); // NOPMD
      if (!Swing.isSwingThread()) {
        _polish(_delegate); // NOPMD
      }
    }
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the name of the component.
   *
   * @return the name of the component
   */
  public String getName() {
    return name;
  }

  /**
   * Sets the name of the component.
   *
   * @param name the component name
   */
  public void setName(String name) {
    final String oldName = this.name;
    if (oldName == null) {
      if (name == null) {
        return;
      }
    } else {
      if (oldName.equals(name)) {
        return;
      }
    }
    disposedCheck();
    _setName(_delegate, name);
    this.name = name;
    firePropertyChange(PROPERTY_NAME, oldName, name);
  }

  /**
   * Returns the owner of the component. The component that disposes this
   * component.
   *
   * @return the owner of the component
   */
  public final Component getOwner() {
    return owner;
  }

  /**
   * Returns the parent of the component. The component that contains this
   * component.
   *
   * @return the parent of the component
   */
  public final Container getParent() {
    return parent;
  }

  /**
   * Returns <tt>true</tt> if this component is owner of the specified
   * component.
   *
   * @param component the component to check
   *
   * @return <tt>true</tt> if this component is owner of the specified
   *         component
   */
  public final boolean isOwnerOf(Component component) {
    return ((component != null) && (component.owner == this));
  }

  /**
   * Return an array containing all the components this component currently
   * owns.
   *
   * @return an array containing all the components this component currently
   *         owns
   */
  public Component[] getOwnedComponents() {
    return EMPTY_COMPONENT_ARRAY; // NOFB NOPMD
  }

  /**
   * Returns component's context menu.
   *
   * @return context menu
   */
  public Menu getContextMenu() {
    return contextMenu;
  }

  /**
   * Sets component's context menu.
   *
   * @param contextMenu the context menu
   *
   * @return old context menu
   */
  public Menu setContextMenu(Menu contextMenu) {
    final Menu oldContextMenu = this.contextMenu;
    if (oldContextMenu != contextMenu) { // NOPMD
      disposedCheck();
      releaseComponent(oldContextMenu);
      if (contextMenu == null) {
        disableEvents(SwingEvent.CONTEXT_MENU_EVENT_MASK |
                      SwingEvent.CUSTOM_CONTEXT_MENU_EVENT_MASK);
      } else {
        if (contextMenu.isDisposed()) {
          throw new IllegalArgumentException("Menu disposed");
        }
        acquireFreeComponent(contextMenu);
        enableEvents(SwingEvent.CONTEXT_MENU_EVENT_MASK |
                     SwingEvent.CUSTOM_CONTEXT_MENU_EVENT_MASK);
      }
      this.contextMenu = contextMenu;
      firePropertyChange(PROPERTY_CONTEXT_MENU, oldContextMenu, contextMenu);
    }
    return oldContextMenu;
  }

  /**
   * Returns <tt>true</tt> if the component can be displayed on the screen.
   *
   * @return <tt>true</tt> if the component can be displayed on the screen
   */
  public boolean isDisplayable() {
    return displayable;
  }

  /**
   * Sets whether or not the component can be displayed on the screen.
   *
   * @param displayable if <tt>true</tt>, the component can be displayed
   */
  public void setDisplayable(boolean displayable) {
    this.displayable = displayable;
  }

  /**
   * Returns <tt>true</tt> if the component is visible.
   *
   * @return <tt>true</tt> if the component is visible
   */
  public boolean isVisible() {
    return visible;
  }

  /**
   * Sets new visibility state.
   *
   * @param visible the new visibility state
   */
  public void setVisible(boolean visible) {
    if (parent == null) {
      if (windowShown) {
        if (this.visible != visible) {
          disposedCheck();
          displayable = visible;
          _setVisible(_delegate, visible);
        }
      } else {
        if (visible) {
          disposedCheck();
          windowShown = true;
          displayable = true;
          _showWindow(_delegate);
        } else if (displayable) {
          displayable = false;
          _setVisible(_delegate, false);
        }
      }
    } else {
      if (this.visible != visible) {
        disposedCheck();
        displayable = visible;
        _setVisible(_delegate, visible);
      }
      if (displayable && !visible) {  // TODO: check - could be a bug
        disposedCheck();
        displayable = false;
        _setVisible(_delegate, false);
      }
    }
  }

  /**
   * Returns <tt>true</tt> if the component is enabled.
   *
   * @return <tt>true</tt> if the component is enabled
   */
  public boolean isEnabled() {
    return enabled;
  }

  /**
   * Enables or disables the component.
   *
   * @param enabled if <tt>true</tt> the component is enabled; otherwise the
   *                component is disabled
   */
  public void setEnabled(boolean enabled) {
    if (this.enabled != enabled) {
      disposedCheck();
      _setEnabled(_delegate, enabled);
    }
  }

  /**
   * Returns whether or not this component can be focused.
   *
   * @return whether or not this component can be focused
   */
  public boolean isFocusable() {
    return focusable;
  }

  /**
   * Sets focusable state of the component.
   *
   * @param focusable if <tt>true</tt>, the component can be focused
   */
  public void setFocusable(boolean focusable) {
    final boolean oldFocusable = this.focusable;
    if (oldFocusable != focusable) {
      disposedCheck();
      _setFocusable(_delegate, focusable);
      this.focusable = focusable;
    }
  }

  /**
   * Returns <tt>true</tt> if this component is the focus owner.
   *
   * @return <tt>true</tt> if this component is the focus owner
   */
  public boolean isFocusOwner() {
    return focused;
  }

  /**
   * Requests the focus.
   */
  public void requestFocus() {
    if (focusable && !focused) {
      disposedCheck();
      _requestFocus(_delegate);
    }
  }

  /**
   * Returns current x coordinate.
   *
   * @return current x coordinate
   */
  public int getX() {
    return bounds.getX();
  }

  /**
   * Returns current y coordinate.
   *
   * @return current y coordinate
   */
  public int getY() {
    return bounds.getY();
  }

  /**
   * Returns location of the component.
   *
   * @return location of the component
   */
  public Point getLocation() {
    return bounds.getLocation();
  }

  /**
   * Sets current location of the component.
   *
   * @param x the new x coordinate of the component
   * @param y the new y coordinate of the component
   */
  public void setLocation(int x, int y) {
    if ((bounds.getX() != x) || (bounds.getY() != y)) {
      disposedCheck();
      _setLocation(_delegate, x, y);
      bounds = bounds.setLocation(x, y);
    }
  }

  /**
   * Sets current location of the component.
   *
   * @param location the new location of the component
   */
  public void setLocation(Point location) {
    setLocation(location.getX(), location.getY());
  }

  /**
   * Returns current width of the component.
   *
   * @return current width of the component
   */
  public int getWidth() {
    return bounds.getWidth();
  }

  /**
   * Returns current height of the component.
   *
   * @return current height of the component
   */
  public int getHeight() {
    return bounds.getHeight();
  }

  /**
   * Returns the size of this component.
   *
   * @return the size of this component
   */
  public Dimension getSize() {
    return bounds.getSize();
  }

  /**
   * Sets size of the component.
   *
   * @param width the new <tt>width</tt> of the component
   * @param height the new <tt>height</tt> of the component
   */
  public void setSize(int width, int height) {
    if ((bounds.getWidth() != width) || (bounds.getHeight() != height)) {
      disposedCheck();
      _setSize(_delegate, width, height);
      bounds = bounds.setSize(width, height);
    }
  }

  /**
   * Sets size of the component.
   *
   * @param size the size
   */
  public void setSize(Dimension size) {
    setSize(size.getWidth(), size.getHeight());
  }

  /**
   * Returns bounds of the component.
   *
   * @return bounds of the component
   */
  public Rectangle getBounds() {
    return bounds;
  }

  /**
   * Moves and resizes the component.
   *
   * @param x the new x coordinate of the component
   * @param y the new y coordinate of the component
   * @param width the new width of the component
   * @param height the new height of the component
   */
  public void setBounds(int x, int y, int width, int height) {
    final Rectangle bounds = this.bounds;
    if ((bounds.getX() != x) || (bounds.getY() != y) ||
        (bounds.getWidth() != width) || (bounds.getHeight() != height)) {
      disposedCheck();
      _setBounds(_delegate, x, y, width, height);
      this.bounds = new Rectangle(x, y, width, height);
    }
  }

  /**
   * Moves and resizes the component.
   *
   * @param bounds the new bounds
   */
  public void setBounds(Rectangle bounds) {
    setBounds(bounds.getX(), bounds.getY(), bounds.getWidth(),
              bounds.getHeight());
  }

  /**
   * Returns preferred size of the component.
   *
   * @return preferred size of the component
   */
  public Dimension getPreferredSize() {
    if (!preferredSizeSet && (!valid || (preferredSize == null))) {
      disposedCheck();
      preferredSize = _getPreferredSize(_delegate);
      if (insets != null) {
        preferredSize = preferredSize.enlarge(insets.getLeft() +
                                              insets.getRight(),
                                              insets.getTop() +
                                              insets.getBottom());
      }
    }
    return preferredSize;
  }

  /**
   * Sets preferred size of the component.
   *
   * @param preferredSize the preferred size
   */
  public void setPreferredSize(Dimension preferredSize) {
    final Dimension oldPreferredSize = this.preferredSize;
    if (oldPreferredSize == null) {
      if (preferredSize == null) {
        return;
      }
    } else {
      if (oldPreferredSize.equals(preferredSize)) {
        return;
      }
    }
    if (preferredSize == null) {
      preferredSizeSet = false;
    } else {
      preferredSizeSet = true;
    }
    this.preferredSize = preferredSize;
    invalidate();
    firePropertyChange(PROPERTY_PREFERRED_SIZE, oldPreferredSize,
                       preferredSize);
  }

  /**
   * Sets preferred size of the component.
   *
   * @param width the preferred width
   * @param height the preferred height
   */
  public void setPreferredSize(int width, int height) {
    setPreferredSize(new Dimension(width, height));
  }

  /**
   * Returns maximum size of the component.
   *
   * @return maximum size of the component
   */
  public Dimension getMaximumSize() {
    if (maximumSize == null) {
      return Dimension.INFINITE_DIMESNION;
    }
    return maximumSize;
  }

  /**
   * Sets maximum size.
   *
   * @param maximumSize the maximum size
   */
  public void setMaximumSize(Dimension maximumSize) {
    final Dimension oldMaximumSize = this.maximumSize;
    if (oldMaximumSize == null) {
      if (maximumSize == null) {
        return;
      }
    } else {
      if (oldMaximumSize.equals(maximumSize)) {
        return;
      }
    }
    disposedCheck();
    if (maximumSize == null) {
      _setMaximumSize(_delegate, Integer.MAX_VALUE, Integer.MAX_VALUE);
    } else {
      _setMaximumSize(_delegate, maximumSize.getWidth(),
                      maximumSize.getHeight());
    }
    this.maximumSize = maximumSize;
    invalidate();
    firePropertyChange(PROPERTY_MAXIMUM_SIZE, oldMaximumSize, maximumSize);
  }

  /**
   * Sets maximum size.
   *
   * @param width the maximum width
   * @param height the maximum height
   */
  public void setMaximumSize(int width, int height) {
    setMaximumSize(new Dimension(width, height));
  }

  /**
   * Returns minimum size of the component.
   *
   * @return minimum size of the component
   */
  public Dimension getMinimumSize() {
    if (minimumSize == null) {
      return Dimension.ZERO_DIMESNION;
    }
    return minimumSize;
  }

  /**
   * Sets minimum size.
   *
   * @param minimumSize the minimum size
   */
  public void setMinimumSize(Dimension minimumSize) {
    final Dimension oldMinimumSize = this.minimumSize;
    if (oldMinimumSize == null) {
      if (minimumSize == null) {
        return;
      }
    } else {
      if (oldMinimumSize.equals(minimumSize)) {
        return;
      }
    }
    disposedCheck();
    if (minimumSize == null) {
      _setMinimumSize(_delegate, 0, 0);
    } else {
      int minimumWidth = minimumSize.getWidth();
      int minimumHeight = minimumSize.getHeight();
      if ((minimumWidth < 0) || (minimumHeight < 0)) {
        throw new IllegalArgumentException("Invalid minimum size");
      }
      _setMinimumSize(_delegate, minimumWidth, minimumHeight);
    }
    this.minimumSize = minimumSize;
    invalidate();
    firePropertyChange(PROPERTY_MINIMUM_SIZE, oldMinimumSize, minimumSize);
  }

  /**
   * Sets minimum size.
   *
   * @param width the minimum width
   * @param height the minimum height
   */
  public void setMinimumSize(int width, int height) {
    setMinimumSize(new Dimension(width, height));
  }

  /**
   * Sets fixed size.
   *
   * @param fixedSize the fixed size
   */
  public void setFixedSize(Dimension fixedSize) {
    final Dimension oldFixedSize = this.fixedSize;
    if (oldFixedSize == null) {
      if (fixedSize == null) {
        return;
      }
    } else {
      if (oldFixedSize.equals(fixedSize)) {
        return;
      }
    }
    disposedCheck();
    if (fixedSize == null) {
      resizable = true;
      _setFixedSize(_delegate, 0, 0);
    } else {
      resizable = false;
      _setFixedSize(_delegate, fixedSize.getWidth(), fixedSize.getHeight());
    }
    this.fixedSize = fixedSize;
    invalidate();
    firePropertyChange(PROPERTY_FIXED_SIZE, oldFixedSize, minimumSize);
  }

  /**
   * Sets fixed size.
   *
   * @param width the fixed width
   * @param height the fixed height
   */
  public void setFixedSize(int width, int height) {
    setFixedSize(new Dimension(width, height));
  }

  /**
   * Returns <tt>true</tt> if the component is resizable.
   *
   * @return <tt>true</tt> if the component is resizable
   */
  public boolean isResizable() {
    return resizable;
  }

  /**
   * Sets resizable status.
   *
   * @param resizable if <tt>true</tt> the component is resizable; otherwise
   *                  <tt>false</tt>
   */
  public void setResizable(boolean resizable) {
    if (this.resizable != resizable) {
      if (resizable) {
        setFixedSize(null);
      } else {
        setFixedSize(getSize());
      }
    }
  }

  /**
   * Returns insets of this component.
   *
   * @return insets of this component
   */
  public Insets getInsets() {
    if (insets != null) {
      return insets;
    }
    return Insets.ZERO_INSEST;
  }

  /**
   * Sets insets for this component.
   *
   * @param insets the insets of the component
   */
  public void setInsets(Insets insets) {
    final Insets oldInsets = this.insets;
    if (oldInsets == null) {
      if (insets == null) {
        return;
      }
    } else {
      if (oldInsets.equals(insets)) {
        return;
      }
    }
    this.insets = insets;
    invalidate();
    firePropertyChange(PROPERTY_INSETS, oldInsets, insets);
  }

  /**
   * Sets insets for this component.
   *
   * @param top the inset from the top
   * @param left the inset from the left
   * @param bottom the inset from the bottom
   * @param right the inset from the right
   */
  public void setInsets(int top, int left, int bottom, int right) {
    setInsets(new Insets(top, left, bottom, right));
  }

  /**
   * Returns <tt>true</tt> if component is valid.
   *
   * @return <tt>true</tt> if component is valid
   */
  public boolean isValid() {
    return valid;
  }

  /**
   * Invalidates the component.
   */
  public void invalidate() {
    if (valid) {
      valid = false;
      if (parent != null) {
        parent.invalidate();
      }
    }
  }

  /**
   * Validates the component.
   */
  public void validate() {
    valid = true;
  }

  /**
   * Revalidates the component.
   */
  public void revalidate() {
    invalidate();
    validate();
  }

  /**
   * Returns <tt>true</tt> if the background color has been explicitly set.
   *
   * @return <tt>true</tt> if the background color has been explicitly set
   */
  public boolean isBackgroundSet() {
    return backgroundSet;
  }

  /**
   * Returns background color of the component.
   *
   * @return background color of the component
   */
  public Color getBackground() {
    if (backgroundSet) {
      return background;
    }
    if (background == null) {
      disposedCheck();
      background = _getBackground(_delegate, Color.class);
    }
    return background;
  }

  /**
   * Sets background color of the component.
   *
   * @param background the background color of the component
   */
  public void setBackground(Color background) {
    final Color oldBackground = this.background;
    if (oldBackground == null) {
      if (background == null) {
        return;
      }
    } else {
      if (oldBackground.equals(background)) {
        return;
      }
    }
    disposedCheck();
    if (background == null) {
      _setNullBackground(_delegate);
      backgroundSet = false;
    } else {
      _setBackground(_delegate, background.getARGB());
      backgroundSet = true;
    }
    this.background = background;
    firePropertyChange(PROPERTY_BACKGROUND, oldBackground, background);
    setOpaque(backgroundSet);
  }

  /**
   * Returns <tt>true</tt> if the background color has been explicitly set.
   *
   * @return <tt>true</tt> if the background color has been explicitly set
   */
  public boolean isForegroundSet() {
    return foregroundSet;
  }

  /**
   * Returns foreground color of the component.
   *
   * @return foreground color of the component
   */
  public Color getForeground() {
    if (foregroundSet) {
      return foreground;
    }
    if (foreground == null) {
      disposedCheck();
      foreground = _getForeground(_delegate, Color.class);
    }
    return foreground;
  }

  /**
   * Sets foreground color of the component.
   *
   * @param foreground the background color of the component
   */
  public void setForeground(Color foreground) {
    final Color oldForeground = this.foreground;
    if (oldForeground == null) {
      if (foreground == null) {
        return;
      }
    } else {
      if (oldForeground.equals(foreground)) {
        return;
      }
    }
    disposedCheck();
    if (foreground == null) {
      _setNullForeground(_delegate);
      foregroundSet = false;
    } else {
      _setForeground(_delegate, foreground.getARGB());
      foregroundSet = true;
    }
    this.foreground = foreground;
    firePropertyChange(PROPERTY_FOREGROUND, oldForeground, foreground);
  }

  /**
   * Returns <tt>true</tt> if component is opaque.
   *
   * @return <tt>true</tt> if component is opaque
   */
  public boolean isOpaque() {
    return opaque;
  }

  /**
   * Sets whether or not component paints all background.
   *
   * @param opaque <tt>true</tt> if the component should paint all background
   */
  public void setOpaque(boolean opaque) {
    final boolean oldOpaque = this.opaque;
    if (oldOpaque != opaque) {
      disposedCheck();
      _setOpaque(_delegate, opaque);
      this.opaque = opaque;
      firePropertyChange(PROPERTY_OPAQUE, oldOpaque, opaque);
    }
  }

  /**
   * Returns <tt>true</tt> if the font is explicitly set.
   *
   * @return <tt>true</tt> if the font is explicitly set
   */
  public boolean isFontSet() {
    return fontSet;
  }

  /**
   * Returns font of this component.
   *
   * @return font of this component
   */
  public Font getFont() {
    if (fontSet) {
      return font;
    }
    if (font == null) {
      disposedCheck();
      font = _getFont(_delegate, Font.class);
    }
    return font;
  }

  /**
   * Sets component's font.
   *
   * @param font the font
   */
  public void setFont(Font font) {
    final Font oldFont = this.font;
    if (oldFont == null) {
      if (font == null) {
        return;
      }
    } else {
      if (oldFont.equals(font)) {
        return;
      }
    }
    disposedCheck();
    if (font == null) {
      _setFont(_delegate, null);
      fontSet = false;
    } else {
      _setFont(_delegate, font);
      fontSet = true;
    }
    this.font = font;
    if (fontMetrics != null) {
      fontMetrics.dispose();
      fontMetrics = null;
    }
    if (oldFont != null) {
      oldFont.dispose();
    }
    clearCache();
    invalidate();
    firePropertyChange(PROPERTY_FONT, oldFont, font);
  }

  /**
   * Returns font metrics of the component's font.
   *
   * @return font metrics of the component's font
   */
  public FontMetrics getFontMetrics() {
    if (fontMetrics == null) {
      fontMetrics = new FontMetrics(getFont());
    }
    return fontMetrics;
  }

  /**
   * Returns style sheet.
   *
   * @return style sheet
   */
  public StyleSheet getStyleSheet() {
    return styleSheet;
  }

  /**
   * Sets style sheet.
   *
   * @param styleSheet the style sheet
   */
  public void setStyleSheet(StyleSheet styleSheet) {
    final StyleSheet oldStyleSheet = this.styleSheet;
    if (oldStyleSheet == null) {
      if (styleSheet == null) {
        return;
      }
    } else {
      if (oldStyleSheet.equals(styleSheet)) {
        return;
      }
    }
    disposedCheck();
    if ((styleSheet == null) || !styleSheet.isValid()) {
      _setStyleSheet(_delegate, null);
      removeStyleSheetProperties();
    } else {
      setStyleSheetProperties();
      _setStyleSheet(_delegate, styleSheet.getStyleSheetText());
    }
    this.styleSheet = styleSheet;
    invalidate();
    firePropertyChange(PROPERTY_STYLE_SHEET, oldStyleSheet, styleSheet);
  }

  /**
   * Sets style sheet.
   *
   * @param styleSheetText the style sheet text
   */
  public void setStyleSheet(String styleSheetText) {
    setStyleSheet(new StyleSheet(styleSheetText));
  }

  /**
   * Removes style sheet.
   */
  public void removeStyleSheet() {
    setStyleSheet((StyleSheet)null);
  }

  /**
   * Returns <tt>true</tt> if the cursor is explicitly set.
   *
   * @return <tt>true</tt> if the cursor is explicitly set
   */
  public boolean isCursorSet() {
    return cursorSet;
  }

  /**
   * Returns cursor.
   *
   * @return cursor
   */
  public Cursor getCursor() {
    if (cursorSet) {
      return cursor;
    }
    if (cursor == null) {
      disposedCheck();
      cursor = Cursor.getPredefinedCursor(_getCursor(_delegate));
    }
    return cursor;
  }

  /**
   * Sets cursor.
   *
   * @param cursor the cursor
   */
  public void setCursor(Cursor cursor) {
    final Cursor oldCursor = this.cursor;
    if (oldCursor == null) {
      if (cursor == null) {
        return;
      }
    } else {
      if (oldCursor.equals(cursor)) {
        return;
      }
    }
    disposedCheck();
    if (cursor == null) {
      _setCursor(_delegate, -1);
      cursorSet = false;
    } else {
      _setCursor(_delegate, cursor.type);
      cursorSet = true;
    }
    this.cursor = cursor;
  }

  /**
   * Returns <tt>true</tt> if component is painting itself.
   *
   * @return <tt>true</tt> if component is painting itself
   */
  public boolean isPainting() {
    return (_graphics != null);
  }

  /**
   * Repaints the component.
   */
  public void repaint() {
    if (visible) {
      disposedCheck();
      _repaint(_delegate);
    }
  }

  /**
   * Repaints the component region.
   *
   * @param x the x coordinate of the region
   * @param y the y coordinate of the region
   * @param width the width of the region
   * @param height the height of the region
   */
  public void repaint(int x, int y, int width, int height) {
    disposedCheck();
    _repaint(_delegate, x, y, width, height);
  }

  /**
   * Repaints the component region.
   *
   * @param region the component region
   */
  public void repaint(Rectangle region) {
    repaint(region.getX(), region.getY(), region.getWidth(),
            region.getHeight());
  }

  /**
   * Returns <tt>true</tt> if updates are enabled.
   *
   * @return <tt>true</tt> if updates are enabled
   */
  public boolean isUpdatesEnabled() {
    return updatesEnabled;
  }

  /**
   * Enables/disables updates.
   *
   * @param enabled it <tt>true</tt> updates are enabled
   */
  public void setUpdatesEnabled(boolean enabled) {
    if (updatesEnabled != enabled) {
      disposedCheck();
      _setUpdatesEnabled(_delegate, enabled);
      updatesEnabled = enabled;
    }
  }

  /**
   * Updates the component.
   */
  public void update() {
    disposedCheck();
    _update(_delegate);
  }

  /**
   * Updates the component region.
   *
   * @param x the x coordinate of the region
   * @param y the y coordinate of the region
   * @param width the width of the region
   * @param height the height of the region
   */
  public void update(int x, int y, int width, int height) {
    disposedCheck();
    _update(_delegate, x, y, width, height);
  }

  /**
   * Updates the component region.
   *
   * @param region the component region
   */
  public void update(Rectangle region) {
    update(region.getX(), region.getY(), region.getWidth(), region.getHeight());
  }

  /**
   * Returns <tt>true</tt> if locale is explicitly set.
   *
   * @return <tt>true</tt> if locale is explicitly set
   */
  public boolean isLocaleSet() {
    return (locale != null);
  }

  /**
   * Returns the locale of the component.
   *
   * @return the locale of the component
   */
  public Locale getLocale() {
    if (locale != null) {
      return locale;
    }
    if (parent != null) {
      return parent.getLocale();
    }
    return Swing.getLocale();
  }

  /**
   * Sets the locale of the component.
   *
   * @param locale the locale of the component
   */
  public void setLocale(Locale locale) {
    final Locale oldLocale = this.locale;
    if (oldLocale == null) {
      if (locale == null) {
        return;
      }
    } else {
      if (oldLocale.equals(locale)) {
        return;
      }
    }
    this.locale = locale;
    invalidate();
    firePropertyChange(PROPERTY_LOCALE, oldLocale, locale);
  }

  /**
   * Returns component orientation.
   *
   * @return component orientation
   */
  public ComponentOrientation getComponentOrientation() {
    return componentOrientation;
  }

  /**
   * Returns <tt>true</tt> if orientation is left-to-right.
   *
   * @return <tt>true</tt> if orientation is left-to-right
   */
  public boolean isLeftToRight() {
    return componentOrientation.isLeftToRight();
  }

  /**
   * Sets component orientation.
   *
   * @param orientation the component orientation
   *
   * @throws IllegalArgumentException if component orientation is <tt>null</tt>
   */
  public void setComponentOrientation(ComponentOrientation orientation) {
    if (orientation == null) {
      throw new IllegalArgumentException("Null component orientation");
    }
    if (isOwnerOf(contextMenu)) {
      contextMenu.setComponentOrientation(orientation);
    }
    ComponentOrientation oldOrientation = componentOrientation;
    if (!oldOrientation.equals(orientation)) {
      disposedCheck();
      _setComponentOrientation(_delegate, orientation.value);
      componentOrientation = orientation;
      invalidate();
      firePropertyChange(PROPERTY_COMPONENT_ORIENTATION, oldOrientation,
                         orientation);
    }
  }

  /**
   * Sets left-to-right component orientation.
   */
  public void setLeftToRightComponentOrientation() {
    setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
  }

  /**
   * Sets right-to-left component orientation.
   */
  public void setRightToLeftComponentOrientation() {
    setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
  }

  /**
   * Unsets component orientation.
   */
  public void unsetComponentOrientation() {
    setComponentOrientation(ComponentOrientation.UNKNOWN);
  }

  /**
   * Updates components orientation using current locale.
   */
  public void updateComponentOrientation() {
    setComponentOrientation(ComponentOrientation.getOrientation(getLocale()));
  }

  /**
   * Returns <tt>true</tt> if resource bundle name is explicitly set.
   *
   * @return <tt>true</tt> if resource bundle name is explicitly set
   */
  public boolean isResourceBundleNameSet() {
    return (resourceBundleName != null);
  }

  /**
   * Returns resource bundle name.
   *
   * @return resource bundle name
   */
  public String getResourceBundleName() {
    if (resourceBundleName != null) {
      return resourceBundleName;
    }
    if (parent != null) {
      return parent.getResourceBundleName();
    }
    return null;
  }

  /**
   * Sets resource bundle name.
   *
   * @param resourceBundleName new resource bundle name
   */
  public void setResourceBundleName(String resourceBundleName) {
    final String oldResourceBundleName = this.resourceBundleName;
    if (oldResourceBundleName == null) {
      if (resourceBundleName == null) {
        return;
      }
    } else {
      if (oldResourceBundleName.equals(resourceBundleName)) {
        return;
      }
    }
    this.resourceBundleName = resourceBundleName;
    firePropertyChange(PROPERTY_RESOURCE_BUNDLE_NAME, oldResourceBundleName,
                       resourceBundleName);
  }

  /**
   * Sets default resource bundle name.
   */
  public void setDefaultResourceBundleName() {
    setResourceBundleName(getClass().getName() + "Resources");
  }

  /**
   * Localizes specified string.
   *
   * @param key the resource bundle key
   * @param str the string to be localized
   *
   * @return localized string
   */
  public String localizeString(String key, String str) {
    if (key != null) {
      String rbname = getResourceBundleName();
      if (rbname != null) {
        try {
          ResourceBundle rb = ResourceBundle.getBundle(rbname, getLocale());
          return rb.getString(key);
        } catch (Throwable t) { // NOPMD
          // empty catch block
        }
      }
    }
    return str;
  }

  /**
   * Returns localized string.
   *
   * @param s the string to be localized
   *
   * @return localized string
   */
  public String localizeString(String s) {
    return localizeString(s, s);
  }

  /**
   * Localizes specified object.
   *
   * @param key the resource bundle key
   * @param obj the object to be localized
   *
   * @return localized object
   */
  public Object localizeObject(String key, Object obj) {
    if (key != null) {
      String rbname = getResourceBundleName();
      if (rbname != null) {
        try {
          ResourceBundle rb = ResourceBundle.getBundle(rbname, getLocale());
          return rb.getObject(key);
        } catch (Throwable t) { // NOPMD
          // empty catch block
        }
      }
    }
    return obj;
  }

  /**
   * Localizes specified string array.
   *
   * @param key the resource bundle key
   * @param strings the string array to be localized
   *
   * @return localized string array
   */
  public String[] localizeStrings(String key, String[] strings) {
    if (key != null) {
      String rbname = getResourceBundleName();
      if (rbname != null) {
        try {
          ResourceBundle rb = ResourceBundle.getBundle(rbname, getLocale());
          return rb.getStringArray(key);
        } catch (Throwable t) { // NOPMD
          // empty catch block
        }
      }
    }
    return strings;
  }

  /**
   * Updates localization.
   */
  public void updateLocalization() {
    disposedCheck();
    if (toolTipText != null) {
      _setToolTipText(_delegate, localizeString(toolTipText));
    }
    if (whatsThisText != null) {
      _setWhatsThisText(_delegate, localizeString(whatsThisText));
    }
    if (statusTipText != null) {
      _setStatusTipText(_delegate, localizeString(statusTipText));
    }
    if (isOwnerOf(contextMenu)) {
      contextMenu.updateLocalization();
    }
  }

  /**
   * Returns tooltip text.
   *
   * @return tooltip text
   */
  public String getToolTipText() {
    return toolTipText;
  }

  /**
   * Sets tooltip text.
   *
   * @param text the tooltip text
   */
  public void setToolTipText(String text) {
    String oldToolTipText = toolTipText;
    if (oldToolTipText == null) {
      if (text == null) {
        return;
      }
    } else {
      if (oldToolTipText.equals(text)) {
        return;
      }
    }
    disposedCheck();
    _setToolTipText(_delegate, text);
    toolTipText = text;
    firePropertyChange(PROPERTY_TOOL_TIP_TEXT, oldToolTipText, text);
  }

  /**
   * Returns tooltip text for the specified location. <tt>Null</tt> is returned
   * if there is no tooltip for the location.
   *
   * @param x the x coordinate of the tooltip location
   * @param y the y coordinate of the tooltip location
   *
   * @return tooltip text for the specified location
   */
  public String getToolTipText(int x, int y) {
    return getToolTipText();
  }

  /**
   * Returns status tip text.
   *
   * @return status tip text
   */
  public String getStatusTipText() {
    return statusTipText;
  }

  /**
   * Sets status tip text.
   *
   * @param text the status tip text
   */
  public void setStatusTipText(String text) {
    String oldStatusTipText = statusTipText;
    if (oldStatusTipText == null) {
      if (text == null) {
        return;
      }
    } else {
      if (oldStatusTipText.equals(text)) {
        return;
      }
    }
    disposedCheck();
    _setStatusTipText(_delegate, text);
    statusTipText = text;
    firePropertyChange(PROPERTY_STATUS_TIP_TEXT, oldStatusTipText, text);
  }

  /**
   * Returns <tt>What's This</tt> text.
   *
   * @return <tt>What's This</tt> text
   */
  public String getWhatsThisText() {
    return whatsThisText;
  }

  /**
   * Sets <tt>What's This</tt> text.
   *
   * @param text the <tt>What's This</tt> text
   */
  public void setWhatsThisText(String text) {
    String oldWhatsThisText = whatsThisText;
    if (oldWhatsThisText == null) {
      if (text == null) {
        return;
      }
    } else {
      if (oldWhatsThisText.equals(text)) {
        return;
      }
    }
    disposedCheck();
    _setWhatsThisText(_delegate, text);
    whatsThisText = text;
    firePropertyChange(PROPERTY_WHATS_THIS_TEXT, oldWhatsThisText, text);
  }

  /**
   * Adds component listener.
   *
   * @param listener the component listener
   */
  public void addComponentListener(ComponentListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.COMPONENT_EVENT_MASK);
      listenerList.add(ComponentListener.class, listener);
    }
  }

  /**
   * Removes component listener.
   *
   * @param listener the component listener
   */
  public void removeComponentListener(ComponentListener listener) {
    if (listener != null) {
      listenerList.remove(ComponentListener.class, listener);
      if (listenerList.getListenerCount(ComponentListener.class) == 0) {
        disableEvents(SwingEvent.COMPONENT_EVENT_MASK);
      }
    }
  }

  /**
   * Returns component listeners.
   *
   * @return component listeners
   */
  public ComponentListener[] getComponentListeners() {
    int count = listenerList.getListenerCount(ComponentListener.class);
    ComponentListener[] listeners = new ComponentListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(ComponentListener.class),
                       0, listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds focus listener.
   *
   * @param listener the focus listener
   */
  public void addFocusListener(FocusListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.FOCUS_EVENT_MASK);
      listenerList.add(FocusListener.class, listener);
    }
  }

  /**
   * Removes focus listener.
   *
   * @param listener the focus listener
   */
  public void removeFocusListener(FocusListener listener) {
    if (listener != null) {
      listenerList.remove(FocusListener.class, listener);
      if (listenerList.getListenerCount(FocusListener.class) == 0) {
        disableEvents(SwingEvent.FOCUS_EVENT_MASK);
      }
    }
  }

  /**
   * Returns focus listeners.
   *
   * @return focus listeners
   */
  public FocusListener[] getFocusListener() {
    int count = listenerList.getListenerCount(FocusListener.class);
    FocusListener[] listeners = new FocusListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(FocusListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds key listener.
   *
   * @param listener the key listener
   */
  public void addKeyListener(KeyListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.KEY_EVENT_MASK);
      listenerList.add(KeyListener.class, listener);
    }
  }

  /**
   * Removes key listener.
   *
   * @param listener the mouse listener
   */
  public void removeKeyListener(KeyListener listener) {
    if (listener != null) {
      listenerList.remove(KeyListener.class, listener);
      if (listenerList.getListenerCount(KeyListener.class) == 0) {
        disableEvents(SwingEvent.KEY_EVENT_MASK);
      }
    }
  }

  /**
   * Returns key listeners.
   *
   * @return key listeners
   */
  public KeyListener[] getKeyListeners() {
    int count = listenerList.getListenerCount(KeyListener.class);
    KeyListener[] listeners = new KeyListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(KeyListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds mouse listener.
   *
   * @param listener the mouse listener
   */
  public void addMouseListener(MouseListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.MOUSE_EVENT_MASK);
      listenerList.add(MouseListener.class, listener);
    }
  }

  /**
   * Removes mouse listener.
   *
   * @param listener the mouse listener
   */
  public void removeMouseListener(MouseListener listener) {
    if (listener != null) {
      listenerList.remove(MouseListener.class, listener);
      if (listenerList.getListenerCount(MouseListener.class) == 0) {
        disableEvents(SwingEvent.MOUSE_EVENT_MASK);
      }
    }
  }

  /**
   * Returns mouse listeners.
   *
   * @return mouse listeners
   */
  public MouseListener[] getMouseListeners() {
    int count = listenerList.getListenerCount(MouseListener.class);
    MouseListener[] listeners = new MouseListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(MouseListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds mouse motion listener.
   *
   * @param listener the mouse motion listener
   */
  public void addMouseMotionListener(MouseMotionListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.MOUSE_MOTION_EVENT_MASK);
      listenerList.add(MouseMotionListener.class, listener);
    }
  }

  /**
   * Removes mouse motion listener.
   *
   * @param listener the mouse motion listener
   */
  public void removeMouseMotionListener(MouseMotionListener listener) {
    if (listener != null) {
      listenerList.remove(MouseMotionListener.class, listener);
      if (listenerList.getListenerCount(MouseMotionListener.class) == 0) {
        disableEvents(SwingEvent.MOUSE_MOTION_EVENT_MASK);
      }
    }
  }

  /**
   * Returns mouse motion listeners.
   *
   * @return mouse motion listeners
   */
  public MouseMotionListener[] getMouseMotionListeners() {
    int count = listenerList.getListenerCount(MouseMotionListener.class);
    MouseMotionListener[] listeners = new MouseMotionListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.
                         getListenersArray(MouseMotionListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds mouse wheel listener.
   *
   * @param listener the mouse wheel listener
   */
  public void addMouseWheelListener(MouseWheelListener listener) {
    if (listener != null) {
      enableEvents(SwingEvent.MOUSE_WHEEL_EVENT_MASK);
      listenerList.add(MouseWheelListener.class, listener);
    }
  }

  /**
   * Removes mouse wheel listener.
   *
   * @param listener the mouse wheel listener
   */
  public void removeMouseWheelListener(MouseWheelListener listener) {
    if (listener != null) {
      listenerList.remove(MouseWheelListener.class, listener);
      if (listenerList.getListenerCount(MouseWheelListener.class) == 0) {
        disableEvents(SwingEvent.MOUSE_WHEEL_EVENT_MASK);
      }
    }
  }

  /**
   * Returns mouse wheel listeners.
   *
   * @return mouse wheel listeners
   */
  public MouseWheelListener[] getMouseWheelListeners() {
    int count = listenerList.getListenerCount(MouseWheelListener.class);
    MouseWheelListener[] listeners = new MouseWheelListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(MouseWheelListener.class),
                       0, listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Adds property change listener for all properties.
   *
   * @param listener the property change listener
   */
  public void addPropertyChangeListener(PropertyChangeListener listener) {
    if (changeSupport == null) {
      changeSupport = new PropertyChangeSupport(this);
    }
    changeSupport.addPropertyChangeListener(listener);
  }

  /**
   * Adds property change listener for specified property.
   *
   * @param propertyName the property name
   * @param listener the property change listener
   */
  public void addPropertyChangeListener(String propertyName,
                                        PropertyChangeListener listener) {
    if (changeSupport == null) {
      changeSupport = new PropertyChangeSupport(this);
    }
    changeSupport.addPropertyChangeListener(propertyName, listener);
  }

  /**
   * Removes property change listener for all properties.
   *
   * @param listener the property change listener
   */
  public void removePropertyChangeListener(PropertyChangeListener listener) {
    if ((listener != null) && (changeSupport != null)) {
      changeSupport.removePropertyChangeListener(listener);
      if (!changeSupport.hasListeners()) {
        changeSupport = null;
      }
    }
  }

  /**
   * Removes property change listener for specified property.
   *
   * @param propertyName the property name
   * @param listener the property change listener
   */
  public void removePropertyChangeListener(String propertyName,
                                           PropertyChangeListener listener) {
    if ((propertyName != null) && (listener != null) &&
        (changeSupport != null)) {
      changeSupport.removePropertyChangeListener(propertyName, listener);
      if (!changeSupport.hasListeners()) {
        changeSupport = null;
      }
    }
  }

  /**
   * Returns property change listeners for all properties.
   *
   * @return property change listeners for all properties
   */
  public PropertyChangeListener[] getPropertyChangeListeners() {
    if (changeSupport == null) {
      return PropertyChangeSupport.getEmptyPropertyChangeListenerArray();
    }
    return changeSupport.getPropertyChangeListeners();
  }

  /**
   * Returns property change listeners for specified property.
   *
   * @param propertyName the property name
   *
   * @return property change listeners for specified property
   */
  public PropertyChangeListener[] getPropertyChangeListeners(
                                                         String propertyName) {
    if (changeSupport == null) {
      return PropertyChangeSupport.getEmptyPropertyChangeListenerArray();
    }
    return changeSupport.getPropertyChangeListeners(propertyName);
  }

  /**
   * Returns the value of the property specified by the key.
   *
   * @param key the being queried
   *
   * @return the value of this property or <tt>null</tt> if there is no such
   *         property
   */
  public Object getClientProperty(Object key) {
    if ((key != null) && (clientProperties != null)) {
      return clientProperties.get(key);
    }
    return null;
  }

  /**
   * Puts key/value "client property".
   *
   * @param key the new property key
   * @param value the new property value
   *
   * @throws IllegalArgumentException if the key is <tt>null</tt>
   */
  public void putClientProperty(Object key, Object value) {
    if (key == null) {
      throw new IllegalArgumentException("Null property key");
    }
    if (clientProperties == null) {
      clientProperties = new HashMap(3);
    }
    Object oldValue = clientProperties.get(key);
    if (value == null) {
      clientProperties.remove(key);
      firePropertyChange(key.toString(), oldValue, null);
    } else if (oldValue != null) {
      clientProperties.put(key, value);
      firePropertyChange(key.toString(), oldValue, value);
    }
  }

// Protected Methods. ----------------------------------------------------------

  /**
   * Returns event mask.
   *
   * @return event mask
   */
  protected long getEventMask() {
    return eventMask;
  }

  /**
   * Enables the events specified by event mask parameter to be delivered to
   * this component.
   *
   * @param eventsToEnable the events to be enabled
   */
  protected void enableEvents(long eventsToEnable) {
    disposedCheck();
    long oldEventMask = eventMask;
    eventMask |= eventsToEnable;
    filterEvents();
    if (eventMask != oldEventMask) {
      _setEnabledEvents(_delegate, eventMask);
    }
  }

  /**
   * Disables the events specified by event mask parameter from being delivered
   * to this component.
   *
   * @param eventsToDisable the events to be disabled
   */
  protected void disableEvents(long eventsToDisable) {
    disposedCheck();
    long oldEventMask = eventMask;
    eventMask &= ~eventsToDisable;
    filterEvents();
    if (eventMask != oldEventMask) {
      _setEnabledEvents(_delegate, eventMask);
    }
  }

  /**
   * Returns <tt>true</tt> if the specified event is enabled.
   *
   * @param event the event
   *
   * @return <tt>true</tt> if the specified event is enabled
   */
  protected boolean eventEnabled(long event) {
    return ((eventMask & event) != 0);
  }

  /**
   * Process component event.
   *
   * @param event the key event
   */
  protected void processComponentEvent(ComponentEvent event) {
    int count = listenerList.getListenerCount(ComponentListener.class);
    if (count > 0) {
      ComponentListener[] listeners = (ComponentListener[])listenerList.
                                    getListenersArray(ComponentListener.class);
      switch(event.getID()) { // NOPMD
        case ComponentEvent.COMPONENT_MOVED:
          for (int i = count; --i >= 0;) {
            listeners[i].componentMoved(event);
          }
          break;
        case MouseEvent.COMPONENT_RESIZED:
          for (int i = count; --i >= 0;) {
            listeners[i].componentResized(event);
          }
          break;
        case MouseEvent.COMPONENT_SHOWN:
          for (int i = count; --i >= 0;) {
            listeners[i].componentShown(event);
          }
          break;
        case MouseEvent.COMPONENT_HIDDEN:
          for (int i = count; --i >= 0;) {
            listeners[i].componentHidden(event);
          }
          break;
      }
    }
  }

  /**
   * Process style change.
   */
  protected void processStyleChange() {
    // empty method
  }

  /**
   * Process focus event.
   *
   * @param event the focus event
   */
  protected void processFocusEvent(FocusEvent event) {
    int count = listenerList.getListenerCount(FocusListener.class);
    if (count > 0) {
      FocusListener[] listeners = (FocusListener[])listenerList.
                                        getListenersArray(FocusListener.class);
      switch(event.getID()) { // NOPMD
        case FocusEvent.FOCUS_GAINED:
          for (int i = count; --i >= 0;) {
            listeners[i].focusGained(event);
          }
          break;
        case FocusEvent.FOCUS_LOST:
          for (int i = count; --i >= 0;) {
            listeners[i].focusLost(event);
          }
          break;
      }
    }
  }

  /**
   * Process key event.
   *
   * @param event the key event
   */
  protected void processKeyEvent(KeyEvent event) {
    int count = listenerList.getListenerCount(KeyListener.class);
    if (count > 0) {
      KeyListener[] listeners = (KeyListener[])listenerList.
                                    getListenersArray(KeyListener.class);
      switch(event.getID()) { // NOPMD
        case KeyEvent.KEY_PRESSED:
          for (int i = count; --i >= 0;) {
            listeners[i].keyPressed(event);
          }
          break;
        case KeyEvent.KEY_RELEASED:
          for (int i = count; --i >= 0;) {
            listeners[i].keyReleased(event);
          }
          break;
        case KeyEvent.KEY_TYPED:
          for (int i = count; --i >= 0;) {
            listeners[i].keyTyped(event);
          }
          break;
      }
    }
  }

  /**
   * Process mouse event.
   *
   * @param event the mouse event
   */
  protected void processMouseEvent(MouseEvent event) {
    int count = listenerList.getListenerCount(MouseListener.class);
    if (count > 0) {
      MouseListener[] listeners = (MouseListener[])listenerList.
                                      getListenersArray(MouseListener.class);
      switch(event.getID()) { // NOPMD
        case MouseEvent.MOUSE_PRESSED:
          for (int i = count; --i >= 0;) {
            listeners[i].mousePressed(event);
          }
          break;
        case MouseEvent.MOUSE_RELEASED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseReleased(event);
          }
          break;
        case MouseEvent.MOUSE_CLICKED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseClicked(event);
          }
          break;
        case MouseEvent.MOUSE_ENTERED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseEntered(event);
          }
          break;
        case MouseEvent.MOUSE_EXITED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseExited(event);
          }
          break;
      }
    }
  }

  /**
   * Process context menu event.
   *
   * @param event the context menu event
   */
  protected void processContextMenuEvent(ContextMenuEvent event) {
    if (contextMenu != null) {
      contextMenu.show(event.getGlobalX(), event.getGlobalY());
    }
  }

  /**
   * Process mouse motion event.
   *
   * @param event the mouse motion event
   */
  protected void processMouseMotionEvent(MouseEvent event) {
    int count = listenerList.getListenerCount(MouseMotionListener.class);
    if (count > 0) {
      MouseMotionListener[] listeners =
          (MouseMotionListener[])listenerList.
                                   getListenersArray(MouseMotionListener.class);
      switch(event.getID()) { // NOPMD
        case MouseEvent.MOUSE_MOVED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseMoved(event);
          }
          break;
        case MouseEvent.MOUSE_DRAGGED:
          for (int i = count; --i >= 0;) {
            listeners[i].mouseDragged(event);
          }
          break;
      }
    }
  }

  /**
   * Process mouse wheel event.
   *
   * @param event the mouse wheel event
   */
  protected void processMouseWheelEvent(MouseWheelEvent event) {
    int count = listenerList.getListenerCount(MouseWheelListener.class);
    if (count > 0) {
      MouseWheelListener[] listeners =
          (MouseWheelListener[])listenerList.
                                  getListenersArray(MouseWheelListener.class);
      for (int i = count; --i >= 0;) {
        listeners[i].mouseWheelMoved(event);
      }
    }
  }

  /**
   * Paints the component.
   *
   * @param g the graphics object
   *
   * @throws IllegalStateException if method is called outside paint event
   *         handling
   */
  protected void paintComponent(Graphics g) {
    SwingGraphics graphics = _graphics;
    if ((graphics != null) && (graphics == g)) { // NOPMD
      _paintComponent(_delegate, graphics._graphicsPointer);
    }
  }

  /**
   * Disposes the component.
   */
  protected void disposeComponent() {
    // empty method
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property
   * @param newValue the new value of the property
   */
  protected void firePropertyChange(String propertyName, Object oldValue,
                                    Object newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a boolean)
   * @param newValue the new value of the property (as a boolean)
   */
  protected void firePropertyChange(String propertyName, boolean oldValue,
                                    boolean newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a byte)
   * @param newValue the new value of the property (as a byte)
   */
  protected void firePropertyChange(String propertyName, byte oldValue,
                                    byte newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a char)
   * @param newValue the new value of the property (as a char)
   */
  protected void firePropertyChange(String propertyName, char oldValue,
                                  char newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a short)
   * @param newValue the new value of the property (as a short)
   */
  protected void firePropertyChange(String propertyName, short oldValue,
                                    short newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as an int)
   * @param newValue the new value of the property (as an int)
   */
  protected void firePropertyChange(String propertyName, int oldValue,
                                    int newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a long)
   * @param newValue the new value of the property (as a long)
   */
  protected void firePropertyChange(String propertyName, long oldValue,
                                    long newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a float)
   * @param newValue the new value of the property (as a float)
   */
  protected void firePropertyChange(String propertyName, float oldValue,
                                    float newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a double)
   * @param newValue the new value of the property (as a double)
   */
  protected void firePropertyChange(String propertyName, double oldValue,
                                    double newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Returns parameters string.
   *
   * @return parameters string
   */
  protected String paramString() {
    StringBuffer sb = new StringBuffer(80);
    sb.append("name=\"");
    String objeectName = getName();
    if (objeectName != null) {
      sb.append(objeectName);
    }
    sb.append("\",");
    sb.append(bounds.getX());
    sb.append(',');
    sb.append(bounds.getY());
    sb.append(',');
    sb.append(bounds.getWidth());
    sb.append('x');
    sb.append(bounds.getHeight());
    if (!isValid()) {
      sb.append(",invalid");
    }
    if (!isVisible()) {
      sb.append(",hidden");
    }
    if (!isEnabled()) {
      sb.append(",disabled");
    }
    if (minimumSize != null) {
      sb.append(",minimumSize");
      sb.append(minimumSize);
    }
    if (maximumSize != null) {
      sb.append(",maximumSize");
      sb.append(maximumSize);
    }
    if (preferredSizeSet) {
      sb.append(",preferredSize");
      sb.append(preferredSize);
    }
    if (contextMenu != null) {
      sb.append(",contextMenu=");
      sb.append(contextMenu);
    }
    return sb.toString();
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public String toString() {
    StringBuffer sb = new StringBuffer(120);
    sb.append(getClass().getName());
    sb.append('[');
    if (isDisposed()) {
      sb.append("disposed");
      String comopnentName = getName();
      if (comopnentName != null) {
        sb.append(",name=\"");
        sb.append(comopnentName);
        sb.append('"');
      }
    } else {
      sb.append(paramString());
    }
    sb.append(']');
    return sb.toString();
  }

  /**
   * {@inheritDoc}
   */
  protected final void disposeObject() {
    if (_delegate != 0) {
      if (parent != null) {
        parent.remove(this);
      }
      if (owner != null) {
        owner.releaseComponent(this);
      }
      destroyComponent();
      if (fontMetrics != null) {
        fontMetrics.dispose();
      }
      if (font != null) {
        font.dispose();
      }
      _destroy(_delegate);
      _delegate = 0;
    }
    componentDestroyed();
    disposeComponent();
  }

// Package-Private Methods. ----------------------------------------------------

  void destroyComponent() {
    // empty method
  }

  void componentDestroyed() {
    displayable = false;
    visible = false;
    if (isOwnerOf(contextMenu)) {
      contextMenu.dispose();
    }
  }

  void acquireComponent(Component component) {
    if (component == this) {
      throw new IllegalArgumentException("Self owning");
    }
    Component previousOwner = component.owner;
    if (previousOwner != null) {
      previousOwner.releaseComponent(component);
    }
    component.owner = this;
  }

  void acquireFreeComponent(Component component) {
    if (component.owner == null) {
      if (component == this) {
        throw new IllegalArgumentException("Self owning");
      }
      component.owner = this;
    }
  }

  void releaseComponent(Component component) {
    if ((component != null) && (component.owner == this)) { // NOPMD
      component.owner = null;
    }
  }

  void filterEvents() {
    // empty method
  }

  void setStyleSheetProperties() {
    if (!styleSheetSet) {
      styleSheetSet = true;
      _setStyleSheetProperties(_delegate);
    }
  }

  void removeStyleSheetProperties() {
    if (styleSheetSet) {
      styleSheetSet = false;
      _removeStyleSheetProperties(_delegate);
    }
  }

  void clearCache() {
    if (!preferredSizeSet) {
      preferredSize = null;
    }
    if (fontMetrics != null) {
      fontMetrics.dispose();
      fontMetrics = null;
    }
    if (!fontSet && (font != null)) {
      font.dispose();
      font = null;
    }
    if (!backgroundSet) {
      background = null;
    }
    if (!foregroundSet) {
      foreground = null;
    }
    if (!cursorSet) {
      cursor = null;
    }
  }

// Package-Private "Native Callbacks". -----------------------------------------

  void _visibleChanged(boolean visible) {
    final boolean oldVisible = this.visible;
    if (oldVisible != visible) {
      this.visible = visible;
      invalidate();
      firePropertyChange(PROPERTY_VISIBLE, oldVisible, visible);
      if (eventEnabled(SwingEvent.COMPONENT_EVENT_MASK)) {
        if (visible) {
          processComponentEvent(
                    new ComponentEvent(this, ComponentEvent.COMPONENT_SHOWN));
        } else {
          processComponentEvent(
                    new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN));
        }
      }
    }
  }

  void _enabledChanged(boolean enabled) {
    final boolean oldEnabled = this.enabled;
    if (oldEnabled != enabled) {
      this.enabled = enabled;
      firePropertyChange(PROPERTY_ENABLED, oldEnabled, enabled);
    }
  }

  void _positionChanged(int x, int y) {
    if ((bounds.getX() != x) || (bounds.getY() != y)) {
      bounds = bounds.setLocation(x, y);
      if (eventEnabled(SwingEvent.COMPONENT_EVENT_MASK)) {
        processComponentEvent(
                     new ComponentEvent(this, ComponentEvent.COMPONENT_MOVED));
      }
    }
  }

  void _sizeChanged(int width, int height) {
    if ((bounds.getWidth() != width) || (bounds.getHeight() != height)) {
      bounds = bounds.setSize(width, height);
      invalidate();
      if (eventEnabled(SwingEvent.COMPONENT_EVENT_MASK)) {
        processComponentEvent(
                   new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
      }
    }
  }

  void _paint(long graphicsPointer) {
    SwingGraphics graphics = new SwingGraphics(graphicsPointer);
    _graphics = graphics;
    paintComponent(graphics);
    _graphics = null;
    graphics.dispose();
  }

  void _styleChanged() {
    clearCache();
    invalidate();
    processStyleChange();
  }

  void _styleSheetChanged(boolean removed) {
    if (removed) {
      removeStyleSheetProperties();
    } else {
      setStyleSheetProperties();
    }
    if (isOwnerOf(contextMenu)) {
      contextMenu._styleSheetChanged(removed);
    }
    _polish(_delegate);
  }

  void _focusGained() {
    focused = true;
    if (eventEnabled(SwingEvent.FOCUS_EVENT_MASK)) {
      processFocusEvent(new FocusEvent(this, FocusEvent.FOCUS_GAINED));
    }
  }

  void _focusLost() {
    focused = false;
    if (eventEnabled(SwingEvent.FOCUS_EVENT_MASK)) {
      processFocusEvent(new FocusEvent(this, FocusEvent.FOCUS_LOST));
    }
  }

  void _keyPressed(int modifiers, int keyCode, char keyChar) {
    processKeyEvent(new KeyEvent(this, KeyEvent.KEY_PRESSED,
                                 System.currentTimeMillis(), modifiers, keyCode,
                                 keyChar));
  }

  void _keyReleased(int modifiers, int keyCode, char keyChar) {
    processKeyEvent(new KeyEvent(this, KeyEvent.KEY_RELEASED,
                                 System.currentTimeMillis(), modifiers, keyCode,
                                 keyChar));
  }

  void _keyTyped(int modifiers, int keyCode, char keyChar) {
    processKeyEvent(new KeyEvent(this, KeyEvent.KEY_TYPED,
                                 System.currentTimeMillis(), modifiers, keyCode,
                                 keyChar));
  }

  void _mouseEntered(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_ENTERED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 0, button));
  }

  void _mouseExited(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_EXITED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 0, button));
  }

  void _mousePressed(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_PRESSED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 1, button));
  }

  void _mouseReleased(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_RELEASED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 1, button));
  }

  void _mouseClicked(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_CLICKED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 1, button));
  }

  void _mouseDoubleClicked(int button, int modifiers, int x, int y) {
    processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_CLICKED,
                                     System.currentTimeMillis(), modifiers, x,
                                     y, 2, button));
  }

  void _contextMenuTriggered(int reason, int modifiers, int x, int y,
                             int globalX, int globalY) {
    processContextMenuEvent(new ContextMenuEvent(this,
                                                 System.currentTimeMillis(),
                                                 reason, modifiers, x, y,
                                                 globalX, globalY));
  }

  void _mouseMoved(int button, int modifiers, int x, int y) {
    processMouseMotionEvent(new MouseEvent(this, MouseEvent.MOUSE_MOVED,
                                           System.currentTimeMillis(),
                                           modifiers, x, y, 0, button));
  }

  void _mouseDragged(int button, int modifiers, int x, int y) {
    processMouseMotionEvent(new MouseEvent(this, MouseEvent.MOUSE_DRAGGED,
                                           System.currentTimeMillis(),
                                           modifiers, x, y, 0, button));
  }

  void _mouseWheelMoved(int modifiers, int x, int y, int wheelRotation) {
    processMouseWheelEvent(new MouseWheelEvent(this, MouseEvent.MOUSE_WHEEL,
                                               System.currentTimeMillis(),
                                               modifiers, x, y, 0, 0, 3,
                                               wheelRotation));
  }

// Package-Private Static Native Methods. --------------------------------------

  native long _create();

  native void _setName(long delegate, String name);

  native void _setVisible(long delegate, boolean visible);

  native void _showWindow(long delegate);

  native void _setEnabled(long delegate, boolean enabled);

  native void _setFocusable(long delegate, boolean focusable);

  native void _requestFocus(long delegate);

  native void _setLocation(long delegate, int x, int y);

  native Dimension _getPreferredSize(long delegate);

  native void _setSize(long delegate, int width, int height);

  native void _setMaximumSize(long delegate, int width, int height);

  native void _setMinimumSize(long delegate, int width, int height);

  native void _setFixedSize(long delegate, int width, int height);

  native void _setBounds(long delegate, int x, int y, int width, int height);

  native Color _getBackground(long delegate, Class colorClass);

  native void _setBackground(long delegate, int argb);

  native void _setNullBackground(long delegate);

  native Color _getForeground(long delegate, Class colorClass);

  native void _setForeground(long delegate, int argb);

  native void _setNullForeground(long delegate);

  native void _setOpaque(long delegate, boolean opaque);

  native Font _getFont(long delegate, Class fontClass);

  native void _setFont(long delegate, Font font);

  native void _setStyleSheet(long delegate, String styleSheetText);

  native void _setStyleSheetProperties(long delegate);

  native void _removeStyleSheetProperties(long delegate);

  native int _getCursor(long delegate);

  native void _setCursor(long delegate, int type);

  native void _repaint(long delegate);

  native void _repaint(long delegate, int x, int y, int width, int height);

  native void _update(long delegate);

  native void _update(long delegate, int x, int y, int width, int height);

  native void _setUpdatesEnabled(long delegate, boolean enabled);

  native void _polish(long delegate);

  native void _setComponentOrientation(long delegate, int orientation);

  native void _setToolTipText(long delegate, String text);

  native void _setStatusTipText(long delegate, String text);

  native void _setWhatsThisText(long delegate, String text);

  native void _setEnabledEvents(long delegate, long events);

  native void _paintComponent(long delegate, long graphicsPointer);

  native void _destroy(long delegate);

// Private Static Native Methods. ----------------------------------------------

  private static native void _initialize();

}
